package edu.uta.cfl.engine;

import java.util.ArrayList;
import java.util.Arrays;

import edu.uta.cfl.data.Combo;
import edu.uta.cfl.data.Component;
import edu.uta.cfl.data.Parameter;
import edu.uta.cfl.data.SUT;
import edu.uta.cfl.data.TestSet;
import edu.uta.cfl.util.Constants;

public class ComboManager {

	public static ArrayList<Combo> findFixLenCombosALL(
			ArrayList<Parameter> prms, int length) {
		ArrayList<Combo> output = new ArrayList<Combo>();

		ArrayList<int[]> comboPattern = Combinatorics.getParamCombos(
				prms.size(), length);

		for (int[] pattern : comboPattern) {
			ArrayList<Parameter> invlPrms = new ArrayList<Parameter>();

			for (int i = 0; i < pattern.length; i++)
				if (pattern[i] == 1) {
					Parameter prm = prms.get(i);
					invlPrms.add(prm);
				}
			ArrayList<int[]> invlValue = new ArrayList<int[]>();
			invlValue = Combinatorics.getValueCombos(invlPrms);

			for (int[] invlCombo : invlValue) {
				Combo cmb = new Combo();

				for (int i = 0; i < invlCombo.length; i++) {
					Component cmp = new Component();
					cmp.setParameter(invlPrms.get(i));
					cmp.setValue(invlCombo[i]);
					cmb.getComponent().add(cmp);
				}
				output.add(cmb);
			}
		}
		return output;
	}

	public static ArrayList<Combo> findFixLenCombosTests(
			ArrayList<Parameter> prms, ArrayList<int[]> ts, int length) {
		ArrayList<Combo> output = new ArrayList<Combo>();

		ArrayList<int[]> comboPattern = Combinatorics.getParamCombos(
				prms.size(), length);

		for (int[] pattern : comboPattern) {
			for (int[] tc : ts) {
				Combo cmb = new Combo();
				for (int i = 0; i < pattern.length; i++)
					if (pattern[i] == 1) {
						Component cmp = new Component(prms.get(i), tc[i]);
						cmb.getComponent().add(cmp);
					}
				if (cmbFrSupCmbLst(cmb, output) == 0)
					output.add(cmb);
			}
		}
		return output;
	}

	public static Combo getCombo(int[] cmbArrray, ArrayList<Parameter> prms) {
		Combo cmb = new Combo();
		for (int i = 0; i < cmbArrray.length; i++) {
			if (cmbArrray[i] != -1) {
				Component cmp = new Component();
				cmp.setParameter(prms.get(i));
				cmp.setValue(cmbArrray[i]);
				cmb.getComponent().add(cmp);
			}
		}
		return cmb;
	}

	public static int[] getArrayCombo(Combo cmb, ArrayList<Parameter> prms) {
		int[] cmbArray = new int[prms.size()];
		Arrays.fill(cmbArray, -1);

		for (Component cmp : cmb.getComponent()) {
			int i = prms.indexOf(cmp.getParameter());
			cmbArray[i] = cmp.getValue();
		}
		return cmbArray;
	}

	public static boolean isComboSpc(Combo cmb, TestSet ts,
			ArrayList<Parameter> prms) {
		// extended BEN
		if (cmbInducingPrb(cmb, ts, prms) >= Constants.IP_Threshold)
			return true;
		return false;
	}

	// extended BEN
	public static double cmbInducingPrb(Combo cmb, TestSet ts,
			ArrayList<Parameter> prms) {

		return (double) comboFrequency(cmb, ts.getAllFailTC(), prms)
				/ comboFrequency(cmb, ts.getMatrix(), prms);

	}

	public static int comboFrequency(Combo cmb, ArrayList<int[]> tcList,
			ArrayList<Parameter> prms) {
		int fr = 0;
		for (int[] tc : tcList) {
			if (isCmbInTC(cmb, tc, prms))
				fr++;
		}
		return fr;
	}

	public static ArrayList<int[]> testsContainCmb(Combo cmb,
			ArrayList<int[]> tcList, ArrayList<Parameter> prms) {
		ArrayList<int[]> ts = new ArrayList<int[]>();
		for (int[] tc : tcList) {
			if (isCmbInTC(cmb, tc, prms))
				ts.add(tc);
		}
		return ts;
	}

	public static boolean isCmbInTC(Combo cmb, int[] tc,
			ArrayList<Parameter> prms) {
		boolean exist = true;
		for (Component cmp : cmb.getComponent())
			if (tc[prms.indexOf(cmp.getParameter())] != cmp.getValue())
				exist = false;
		return exist;

	}

	public static String ToString(Combo cmb, ArrayList<Parameter> prms) {
		String output = "(";
		for (Component cmp : cmb.getComponent()) {
			Parameter prm = prms.get(prms.indexOf(cmp.getParameter()));
			output += prm.getName() + "=" + prm.getValues().get(cmp.getValue())
					+ ", ";
		}
		output = output.substring(0, output.lastIndexOf(","));

		return output + ")";

	}

	public static String ToString(int[] cmbArray, ArrayList<Parameter> prms) {
		String output = "";

		Combo cmb = getCombo(cmbArray, prms);

		for (int i = 0; i < cmbArray.length; i++) {
			if (cmbArray[i] == -1)
				output += "-1\t";
			else
				output += prms.get(i).getValues().get(cmbArray[i]) + "\t";
		}

		output += cmb.getEnvRankValue() + "\t";
		output += cmb.getRankValue() + "\t";
		output += cmb.getRank() + "\t";

		for (Component cmp : cmb.getComponent()) {
			output += cmp.getPart1() + "\t";
			output += cmp.getPart2() + "\t";
			output += cmp.getPart3() + "\t";
			output += cmp.getRankValue() + "\t";
		}

		return output;
	}

	public static boolean isComboSetsEqual(ArrayList<Combo> newList,
			ArrayList<Combo> preList) {
		for (Combo cmb : newList)
			if (isCmbInCmbLst(cmb, preList) == -1)
				return false;
		for (Combo cmb : preList)
			if (isCmbInCmbLst(cmb, newList) == -1)
				return false;
		return true;
	}

	// check only parameter and value not rank value...
	public static int isCmbInCmbLst(Combo cmb, ArrayList<Combo> cmbList) {
		for (Combo c : cmbList)
			if (isCmbsEqual(cmb, c))
				return cmbList.indexOf(c);
		return -1;

	}

	public static boolean isCmbsEqual(Combo cmb1, Combo cmb2) {
		if (cmb1.getSize() != cmb2.getSize())
			return false;

		int match = 0;
		for (Component cmp : cmb2.getComponent())
			if (ComponentManager.idxCmpLst(cmp, cmb1.getComponent()) != -1)
				match++;
		if (match == cmb1.getSize())
			return true;

		return false;
	}

	public static int possibleTC(Combo cmb, ArrayList<Parameter> prms) {
		int maxTC = 1;
		for (Parameter prm : prms)
			maxTC *= prm.getDomainSize();

		for (Component cmp : cmb.getComponent())
			maxTC /= cmp.getParameter().getDomainSize();
		return maxTC;
	}

	public static boolean isCmbFailInd(Combo cmb, SUT sut) {
		int cmbFr = ComboManager.comboFrequency(cmb, sut.getTs().getMatrix(),
				sut.getParameters());
		int maxFr = ComboManager.possibleTC(cmb, sut.getParameters());
		if (cmbFr == maxFr) {
			return true;
		}

		return false;
	}

	public static ArrayList<Combo> getTopCombos(ArrayList<Combo> cmbs, int n) {
		if (cmbs.size() < n)
			return cmbs;
		else {
			ArrayList<Combo> output = new ArrayList<Combo>();
			output.addAll(cmbs.subList(0, n - 1));
			return output;
		}
	}

	public static boolean isCmbSubCmb(Combo subCmb, Combo superCmb) {
		for (Component cmp : subCmb.getComponent()) {
			if (!ComponentManager.isComboHas(cmp, superCmb))
				return false;
		}
		return true;
	}

	public static int cmbFrSupCmbLst(Combo subCmb, ArrayList<Combo> superCmbLst) {
		int frequency = 0;
		for (Combo superCmb : superCmbLst) {
			if (isCmbSubCmb(subCmb, superCmb))
				frequency++;
		}
		return frequency;
	}

	public static int possibleCombo(Combo cmb, ArrayList<Parameter> prms) {
		int maxCmb = 0;
		for (Parameter prm : prms)
			maxCmb += prm.getDomainSize();

		for (Component cmp : cmb.getComponent())
			maxCmb -= cmp.getParameter().getDomainSize();
		return maxCmb;
	}

	public static double minSpcSubCmb(Combo subCmb, ArrayList<Combo> superCmbLst) {
		double min = 1000000;
		for (Combo superCmb : superCmbLst) {
			if (isCmbSubCmb(subCmb, superCmb) && superCmb.getRankValue() < min)
				min = superCmb.getRankValue();
		}
		return min;
	}

	public static int maxRankSubCmb(Combo subCmb, ArrayList<Combo> superCmbLst) {
		int max = 0;
		for (Combo superCmb : superCmbLst) {
			if (isCmbSubCmb(subCmb, superCmb) && superCmb.getRank() > max)
				max = superCmb.getRank();
		}
		return max;
	}

	public static double avgSpcSubCmb(Combo subCmb, ArrayList<Combo> superCmbLst) {
		double avg = 0;
		for (Combo superCmb : superCmbLst) {
			if (isCmbSubCmb(subCmb, superCmb))
				avg += superCmb.getRankValue();
		}
		return avg / cmbFrSupCmbLst(subCmb, superCmbLst);
	}

	public static double maxSpcSubCmb(Combo subCmb, ArrayList<Combo> superCmbLst) {
		double max = 0;
		for (Combo superCmb : superCmbLst) {
			if (isCmbSubCmb(subCmb, superCmb) && superCmb.getRankValue() > max)
				max = superCmb.getRankValue();
		}
		return max;
	}

	public static ArrayList<Parameter> getParameter(Combo cmb) {
		ArrayList<Parameter> prms = new ArrayList<Parameter>();
		for (Component cmp : cmb.getComponent()) {
			prms.add(cmp.getParameter());
		}
		return prms;
	}
}
